Elements of Functional Programming in Python

Learn how to how to use the lambda, map, filter and reduce functions in Python to transform data structures.

Python provides features like lambda, filter, map, and reduce that can basically cover most of what you would need to know about Functional Programming.

For detailed analysis, please see the accompanying blog titled:Elements of Functional Programming in Python

The Lambda Expression

Lambda expressions - also known as "anonymous functions" - allow us to create and use a function in a single line. They are useful when we need a short function that will be used only once. 

They are mostly used in conjunction with the map, filter and the sort methods, which we will see later in the article.

Let's write a function in Python, that will compute the value of 5x + 2. The standard approach would be to define a function.


In [5]:
def f(x):
    """Function to compute the value of 5x+2"""
    return 5*x+2
f(3)


Out[5]:
17

Now we would compute the same expression using Lambda functions. To create a lambda expression, we type in the keyword lambda, followed by the inputs. Next, we enter a colon followed by the expression that will be the return value.


In [6]:
lambda x: 5*x+2


Out[6]:
<function __main__.<lambda>(x)>

This lambda function will take the input x and return 5x + 2, just like the earlier function f. There is a problem, however.The lambda is not the name of the function. It is a Python keyword that says - what follows is an anonymous function. So how do we use it? One way is to give it a name. Let us call this lambda expression g. Now, you can use this like any other function.


In [7]:
g = lambda x: 5*x+2
g(3)


Out[7]:
17

Lambda expression with multiple inputs.


In [20]:
# Calculating Harmonic Mean using lambda function
harmonic_mean = lambda x,y,z : 3/(1/x + 1/y + 1/z)**0.5
harmonic_mean(1,2,3)


Out[20]:
2.215646837627989

Lambda expression without inputs.

let's look at a common use of Lambda function where we do not assign it a name. Let's say we have a list of the first seven U.S Presidents and we'd like to sort this list by their last name. We shall create a Lambda function that extracts the last name, and uses that as the sorting value.


In [21]:
# Sorting a List by thr last name using lambda expression

In [22]:
presidents_usa = ["George Washington", "John Adams","Thomas Jefferson","James Madison","James Monroe","John Quincy Adams","Andrew Jackson"]

presidents_usa.sort(key = lambda name: name.split(" ")[-1].lower())
presidents_usa


Out[22]:
['John Adams',
 'John Quincy Adams',
 'Andrew Jackson',
 'Thomas Jefferson',
 'James Madison',
 'James Monroe',
 'George Washington']

The Map Function

The map function applies a function to every item of iterable, yielding the results. When used with lists, Map transforms a given list into a new list by applying the function to all the items in an input_list.

Syntax

map(function_to_apply, iterables)

Usage

Suppose we have a function that computes the volume of a cube, given the value of its edge(a)


In [25]:
def volume(a):
    """volumne of a cube with edge 'a'"""
    return a**3

What if we need to compute the volumes for many different cubes with different edge lengths? 


In [26]:
# Edge length in cm
edges = [1,2,3,4,5]

There are two ways to do this. One by using the direct method and the other by using the map function.


In [27]:
# Calculating the volume of given cubes using Direct Method

volumes = []
for a in edges:
    v = volume(a)
    volumes.append(v)
    
    
volumes


Out[27]:
[1, 8, 27, 64, 125]

Now let's see how we can accomplish this task using a single line of code with the map function.


In [29]:
# Calculating the volume of given cubes using the Map function

map(volume,edges)


Out[29]:
<map at 0x10e9aa9b0>

The map function takes in two arguments. The first is a function, and the second is your list, tuple, or any other iterable object. Here, the map function applies the volume function to each element in the list. 

However, an important thing to note here is that the output of the map function is not a list but a map object, which is actually an iterator over the results. We can, however, turn this into a list by passing the map to the list constructor.


In [31]:
list(map(volume,edges))


Out[31]:
[1, 8, 27, 64, 125]

Example

Let's now see an example which demonstrates the use of lambda function with the map function. We have a list of tuples containing name and heights for 5 people. Each of the height is in centimeters and we need to convert it into feet.

We will first write a converter function using a lambda expression which will accept a tuple as the input and will return a tuple with the same name.


In [32]:
# Convert height from cms to feet : 1 cm = 0.0328 feet
height_in_cms = [('Tom',183),('Daisy',171),('Margaret',179),('Michael',190),('Nick',165)]

#Writing Convertor function using lambda expression
height_in_feet = lambda data: (data[0],round(data[1]*0.0328,1))

#Using the 'Map' function
list(map(height_in_feet,height_in_cms))


Out[32]:
[('Tom', 6.0),
 ('Daisy', 5.6),
 ('Margaret', 5.9),
 ('Michael', 6.2),
 ('Nick', 5.4)]

The Filter Function

The filter function constructs an iterator from those elements of iterable for which function returns true. This means filter function is used to select certain pieces of data from a list, tuple, or other collection of data, hence the name.

Syntax

filter(filter(function, iterable)

Usage

Let's see an example where we want to get the list of all numbers that are greater than 5, from a given input list.


In [33]:
# Filter out all the numbers greater than 5 from a list

my_list = [1,2,3,4,5,6,7,8,9]
output_list = filter(lambda x : x>5, my_list)

list(output_list)


Out[33]:
[6, 7, 8, 9]

Example

Here is a list containing some of the countries in Asia. Notice there are numerous strings that are empty. We'll use the filter function to remove these missing values. We'll simply pass none as the first argument and the second argument is the list of data as before.


In [34]:
# Removing missing values from a list

countries_asia = ["Afghanistan","","Bhutan","","China","","Georgia","","","India"]

list(filter(None,countries_asia))


Out[34]:
['Afghanistan', 'Bhutan', 'China', 'Georgia', 'India']

This filters out all values that are treated as false in a boolean setting.

The Reduce Function

The Reduce function is a bit unusual and in fact, as of Python 3, it is no longer a built-in function. Instead, it has been moved to the functools module.

The 'reduce' function transforms a given list into a single value by applying a function cumulatively to the items of sequence, from left to right,

Syntax

reduce(func, seq)

where reduce continually applies the function func() to the sequence seq and returns a single value.

Usage

Let's illustrate the working of the reduce function with the help of a simple example that computes the product of a list of integers.


In [36]:
# Compute the product of a list of integers using 'reduce'function

from functools import reduce
product = reduce((lambda x,y : x*y), [1,2,3,4,5])

product


Out[36]:
120

The following diagram shows the intermediate steps of the calculation:

The above program can also be written with an explicit for loop which is more clear.Hence Use functools.reduce if you really need it


In [38]:
# Compute the product of a list of integers using a 'For' loop

product = 1
list = [1,2,3,4,5]
for num in list:
    product = product*num
    
product


Out[38]:
120

Example

The reduce function can determine the maximum of a list containing integers in a single line of code. There does exist a built-in function called max() in Python which is normally used for this purpose as max(list_name).


In [42]:
# Determining the maximum number in a given list

from functools import reduce
f = lambda a,b : a if (a>b) else b
reduce(f,[58,69,12,158,698])


Out[42]:
698

List Comprehensions: Alternative to map, filter and reduce

List comprehension is a way to define and create lists in Python. in most cases, list comprehensions let us create lists in a single line of code without worrying about initializing lists or setting up loops. It is also a substitute for the lambda function as well as the functions map(), filter() and reduce(). Some people find it a more pythonic way of writing functions and find it easier to understand.

Syntax


In [44]:
# Creating a list of squares of first 10 numbers using loops 

squares = []
for x in range(10):
    squares.append(x**2)
    
squares


Out[44]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Now let's use list comprehension to achieve the same result in a one liner


In [45]:
# # Creating a list of squares of first 10 using list comprehension

squares = [x**2 for x in range(10)]
squares


Out[45]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Usage

Let's try and replicate the examples used in the above sections with list comprehensions.

List Comprehensions vs Map function

We used map function in conjunction with the lambda function to convert a list of heights from cm to feet. Let's use list comprehensions to achieve the same results.


In [46]:
# Convert height from cms to feet using List Comprehension : 1 cm = 0.0328 feet
height_in_cms = [('Tom',183),('Daisy',171),('Margaret',179),('Michael',190),('Nick',165)]

height_in_feet = [(height[0],round(height[1]*0.0328,1)) for height in height_in_cms]

height_in_feet


Out[46]:
[('Tom', 6.0),
 ('Daisy', 5.6),
 ('Margaret', 5.9),
 ('Michael', 6.2),
 ('Nick', 5.4)]

List Comprehensions vs Filter function

We used the filter function to remove the missing values from a list of Asian countries. Let's use list comprehensions to get the same results.


In [47]:
# Removing missing values from a list

countries_asia = ["Afghanistan","","Bhutan","","China","","Georgia","","","India"]
[country for country in countries_asia if country!=""]


Out[47]:
['Afghanistan', 'Bhutan', 'China', 'Georgia', 'India']

List Comprehensions vs Reduce function

Similarly, we can determine the maximum of a list containing integers easily using generator comprehension instead of using lambda and reduce. Generator expression are similar to list comprehension but with round brackets instead of the square one.


In [49]:
# Determining the maximum number in a given list

numbers = [58,69,12,158,698,956]
max((x) for x in numbers)


Out[49]:
956

In [ ]: